
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <modbus.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <json-c/json.h>

#define SHM_SIZE 1024

typedef struct {
    int key;
    char name[32];
    int addr;
    int type;
    int value;
} ModbusData;

modbus_t *ctx = NULL;
ModbusData *shared_data = NULL;
int shmid;

void init_modbus(const char *ip, int port) {
    ctx = modbus_new_tcp(ip, port);
    if (ctx == NULL) {
        fprintf(stderr, "Unable to create Modbus context\n");
        exit(1);
    }
    
    if (modbus_connect(ctx) == -1) {
        fprintf(stderr, "Modbus connection failed: %s\n", modbus_strerror(errno));
        modbus_free(ctx);
        exit(1);
    }
}

void init_shared_memory() {
    key_t key = ftok("modbus_shm", 65);
    shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);
    if (shmid == -1) {
        perror("shmget");
        exit(1);
    }
    
    shared_data = (ModbusData *)shmat(shmid, NULL, 0);
    if (shared_data == (void *)-1) {
        perror("shmat");
        exit(1);
    }
}

void parse_json_config(const char *filename) {
    struct json_object *root, *modbus, *data, *item;
    size_t n_items, i;
    
    root = json_object_from_file(filename);
    if (!root) {
        fprintf(stderr, "Failed to parse JSON file\n");
        exit(1);
    }
    
    json_object_object_get_ex(root, "modbus", &modbus);
    json_object_object_get_ex(modbus, "data", &data);
    n_items = json_object_array_length(data);
    
    for (i = 0; i < n_items; i++) {
        item = json_object_array_get_idx(data, i);
        struct json_object *j_key, *j_name, *j_addr, *j_type;
        
        json_object_object_get_ex(item, "key", &j_key);
        json_object_object_get_ex(item, "name", &j_name);
        json_object_object_get_ex(item, "addr", &j_addr);
        json_object_object_get_ex(item, "type", &j_type);
        
        shared_data[i].key = json_object_get_int(j_key);
        strncpy(shared_data[i].name, json_object_get_string(j_name), 32);
        shared_data[i].addr = json_object_get_int(j_addr);
        shared_data[i].type = json_object_get_int(j_type);
    }
    
    json_object_put(root);
}

void read_modbus_data() {
    int i = 0;
    while (shared_data[i].key != 0) {
        if (shared_data[i].type == 3) { // Holding register
            uint16_t reg;
            if (modbus_read_registers(ctx, shared_data[i].addr - 40001, 1, &reg) == -1) {
                fprintf(stderr, "Failed to read register: %s\n", modbus_strerror(errno));
            } else {
                shared_data[i].value = reg;
            }
        } else if (shared_data[i].type == 1) { // Coil
            uint8_t coil;
            if (modbus_read_bits(ctx, shared_data[i].addr - 1, 1, &coil) == -1) {
                fprintf(stderr, "Failed to read coil: %s\n", modbus_strerror(errno));
            } else {
                shared_data[i].value = coil;
            }
        }
        i++;
    }
}

void write_modbus_data(int key, int value) {
    int i = 0;
    while (shared_data[i].key != 0) {
        if (shared_data[i].key == key) {
            if (shared_data[i].type == 3) { // Holding register
                if (modbus_write_register(ctx, shared_data[i].addr - 40001, value) == -1) {
                    fprintf(stderr, "Failed to write register: %s\n", modbus_strerror(errno));
                }
            } else if (shared_data[i].type == 1) { // Coil
                if (modbus_write_bit(ctx, shared_data[i].addr - 1, value) == -1) {
                    fprintf(stderr, "Failed to write coil: %s\n", modbus_strerror(errno));
                }
            }
            shared_data[i].value = value;
            break;
        }
        i++;
    }
}

void cleanup() {
    if (ctx) {
        modbus_close(ctx);
        modbus_free(ctx);
    }
    if (shared_data) {
        shmdt(shared_data);
    }
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("Usage: %s <config.json>\n", argv[0]);
        return 1;
    }
    
    init_shared_memory();
    parse_json_config(argv[1]);
    
    struct json_object *root, *mb_dev;
    root = json_object_from_file(argv[1]);
    json_object_object_get_ex(root, "mb_dev", &mb_dev);
    
    const char *ip = json_object_get_string(json_object_object_get(mb_dev, "addr"));
    int port = json_object_get_int(json_object_object_get(mb_dev, "port"));
    
    init_modbus(ip, port);
    
    while (1) {
        read_modbus_data();
        sleep(5); // Adjust based on report period in config
    }
    
    cleanup();
    return 0;
}
